package _index;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.WindowConstants;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;

/**
 * @description: swingtips 例子快捷整合
 * @author: jee
 */
public class SwingTipsPanel extends JPanel {

	private static final long serialVersionUID = 1L;

	public static List<JFrame> frames;

	private JButton invoke;

	private JButton invokeAsSingle;

	private JTextField packageName;

	private JTextArea textArea;

	private JTextField search;

	private JButton reset;

	private JLabel status;

	private int clickLine;

	private JCheckBox enableDbClick;

	private JCheckBox enableFilter;

	private JCheckBox enableBlankReset;

	private JCheckBox enableInner;

	private JCheckBox enableIngoreCase;

	private String content;

	private List<String> lineContents;

	private String ps = "<li>该工具可能仅仅支持百分之80+得Tip示例<br/>" + "invokeInner 是反射尝试实例化后自定义运行,关闭子系统不关闭主系统<br/> "
			+ "invokeSingle 是反射调用main方法,关闭子系统主进程也结束</li>" + "<li>ps:不是因为java-swing-tips的作者问题, 作者的每个tip都是一个gradle小工程,"
			+ " 是因为当时抽取所有静态资源到example文件夹时,未考虑路径问题, "
			+ " 部分例子可能因为逻辑问题而产生加载资源错误,比如:RearrangeToolBarIcon 等,那么可以直接拷贝github 得源文件放入自己ide工具中调试,查看及测试</li> "
			+ "java-swing-tips github网址:<a href=https://github.com/aterai/java-swing-tips >https://github.com/aterai/java-swing-tips</a>";

	public SwingTipsPanel() {
		init();
		render();
		bindEvents();

	}

	/**
	 * 初始化成员对象
	 */
	protected void init() {
		ToolTipManager.sharedInstance().setInitialDelay(500);
		content = ResourceUtil.readUtf8Str("_index/tips.txt");
		lineContents = StrUtil.split(content, '\n', -1);
		System.out.println("tips size==>" + lineContents.size());
		frames = new ArrayList<JFrame>();
		// 作为内部运行
		invoke = new JButton("invokeAsInner");
		invoke.setToolTipText("关闭子系统时不会结束进程,但是某些示例不适用");
		// 作为外部运行
		invokeAsSingle = new JButton("invokeAsSingle");

		packageName = new JTextField(20);
		packageName.setToolTipText("输入包名点击,invoke测试");
		textArea = new JTextArea(content);
		textArea.setBackground(new Color(38, 50, 56));
		textArea.setForeground(new Color(238, 238, 238));
		textArea.setEditable(false);
		status = new JLabel();
		search = new JTextField(50);
		search.setToolTipText("Enter搜索,可以使用通配符* 模糊匹配");
		reset = new JButton("reset");

		enableDbClick = new JCheckBox("启用双击textarea自动打开", true);

		enableFilter = new JCheckBox("搜索移除未匹配选项", true);

		enableBlankReset = new JCheckBox("搜索框内容为空时enter重置", true);

		enableInner = new JCheckBox("inner方式打开", true);

		enableIngoreCase = new JCheckBox("忽略大小写", true);
	}

	/**
	 * render视图
	 */
	protected void render() {

		Box ver = Box.createVerticalBox();
		ver.add(Box.createVerticalStrut(20));

		Box row1 = Box.createHorizontalBox();
		row1.add(Box.createHorizontalStrut(50));
		row1.add(new JLabel("包名 : ", JLabel.RIGHT));
		row1.add(packageName);
		row1.add(Box.createHorizontalStrut(20));
		row1.add(invoke);
		row1.add(invokeAsSingle);
		row1.add(Box.createHorizontalStrut(50));
		ver.add(row1);
		ver.add(Box.createVerticalStrut(20));

		Box row2 = Box.createHorizontalBox();
		row2.add(Box.createHorizontalStrut(50));
		row2.add(new JLabel("搜索 : ", JLabel.RIGHT));
		row2.add(search);
		row2.add(Box.createHorizontalStrut(20));
		row2.add(reset);
		row2.add(enableIngoreCase);
		ver.add(row2);
		ver.add(Box.createVerticalStrut(20));

		Box row3 = Box.createHorizontalBox();
		JScrollPane scrollPane = new JScrollPane(textArea);
		scrollPane.setBorder(BorderFactory.createTitledBorder("SwingTips 支持的包名:"));
		scrollPane.setPreferredSize(new Dimension(800, 400));
		row3.add(scrollPane);
		ver.add(row3);

		Box row5 = Box.createHorizontalBox();
		row5.add(enableDbClick);
		row5.add(enableFilter);
		row5.add(enableBlankReset);
		row5.add(enableInner);
		row5.add(Box.createHorizontalGlue());
		ver.add(row5);
		Box row4 = Box.createHorizontalBox();
		row4.add(new JLabel("LogStatus: "));
		row4.add(Box.createHorizontalStrut(20));
		row4.add(status);
		row4.add(Box.createHorizontalGlue());
		ver.add(row4);
		JEditorPane editorPane = new JEditorPane();
		editorPane.setContentType("text/html");
		editorPane.setText(ps);
		JScrollPane psScrollPane = new JScrollPane(editorPane);
		psScrollPane.setPreferredSize(new Dimension(800, 150));
		ver.add(psScrollPane);
		super.add(ver);
	}

	/**
	 * 绑定事件
	 */
	protected void bindEvents() {
		invoke.addActionListener((e) -> {
			String pack = packageName.getText();
			this.invokeTip(pack);
		});

		invokeAsSingle.addActionListener((e) -> {
			String pack = packageName.getText();
			this.invokeTip(pack, false);
		});

		reset.addActionListener((e) -> {
			search.setText("");
			cleanHighLight();
			textArea.setText(content);
		});
		bindSearchEnter();

		textArea.addCaretListener(new CaretListener() {
			public void caretUpdate(CaretEvent e) {
				if (textArea.getText().trim().length() == 0) {
					return;
				}
				// 计算光标所在行列
				int offset = e.getDot();
				try {
					// 得到光标所在的行数
					clickLine = textArea.getLineOfOffset(offset);
				} catch (BadLocationException e3) {
					e3.printStackTrace();
				}
			}
		});

		textArea.addMouseListener(new MouseAdapter() {

			@Override
			public void mouseClicked(MouseEvent e) {
				if (e.getClickCount() == 2) {// 双击
					String[] lines = textArea.getText().split("\n");
					String dbWord = lines[clickLine];
					packageName.setText(dbWord);
					if (enableDbClick.isSelected()) {
						invokeTip(dbWord);
						setStatus("已打开:" + dbWord);
					} else {
						setStatus("wran:双击未启用!");
					}
				}
			}
		});

	}

	/**
	 * 绑定回车事件
	 */
	private void bindSearchEnter() {
		KeyStroke theEnter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
		ActionMap contentActionMap = search.getActionMap();
		InputMap contentInputMap = search.getInputMap(JComponent.WHEN_FOCUSED);
		contentInputMap.put(theEnter, "search_of_Input");
		contentActionMap.put("search_of_Input", new AbstractAction() {
			private static final long serialVersionUID = -4466720263722636842L;

			@Override
			public void actionPerformed(ActionEvent e) {
				String text = search.getText().trim();

				if (text.length() == 0 && enableBlankReset.isSelected()) {
					textArea.setText(content);
					setStatus("重置搜索");
				}

				if (StrUtil.isBlank(text)) {
					return;
				}
				// 是否忽略大小写
				String reg = enableIngoreCase.isSelected() ? "(?i)" + text : text;
				if (reg.startsWith("*")) {
					reg = ".*" + text.substring(1);
				}
				if (reg.endsWith("*")) {
					reg = reg.substring(0, reg.length() - 1) + ".*";
				}
				System.out.println("reg==>" + reg);
				if (enableFilter.isSelected()) {
					filterMatch(reg, text);
				} else {
					highMatch(reg, text);
				}
			}
		});
	}

	/**
	 * 过滤搜索
	 *
	 * @param reg
	 */
	private void filterMatch(String reg, String originText) {
		Pattern r = Pattern.compile(reg);
		List<String> matchs = lineContents.stream().filter((s) -> {
			Matcher m = r.matcher(s);
			return m.find();
		}).collect(Collectors.toList());
		textArea.setText(matchs.stream().collect(Collectors.joining("\n")));
		setStatus(StrUtil.format("filter search:[{}] 数量:{}", originText, matchs.size()));
	}

	/**
	 * 高亮匹配
	 */
	private void highMatch(String reg, String originText) {
		cleanHighLight();
		// String reg = ".*" + text + ".*";
		Pattern r = Pattern.compile(reg);
		Matcher m = r.matcher(content);
		int count = 0;
		while (m.find()) {
			System.out.println("start:" + m.start() + " end:" + m.end());
			count++;
			renderHighLight(m.start(), m.end());
		}
		setStatus(StrUtil.format("highLight search:[{}] 数量:{}", originText, count));
	}

	/**
	 * 高亮渲染
	 *
	 * @param start
	 * @param end
	 */
	private void renderHighLight(int start, int end) {
		Highlighter highlighter = textArea.getHighlighter();
		DefaultHighlighter.DefaultHighlightPainter p = new DefaultHighlighter.DefaultHighlightPainter(
				new Color(213, 0, 0));
		try {
			highlighter.addHighlight(start, end, p);
		} catch (BadLocationException e1) {
			e1.printStackTrace();
		}
	}

	/**
	 * 清除渲染
	 */
	private void cleanHighLight() {
		textArea.getHighlighter().removeAllHighlights();
	}

	/**
	 * 状态区域
	 *
	 * @param text
	 */
	private void setStatus(String text) {
		status.setText("<html><font color='#FF4D4F' >" + text + "<br/> ");
	}

	public void invokeTip(String packageName) {
		this.invokeTip(packageName, enableInner.isSelected());
	}

	/**
	 * 反射显示tip例子
	 */
	public void invokeTip(String packageName, boolean isInner) {
		ThreadUtil.execAsync(() -> {
			try {
				Class<?> clazz = Class.forName(packageName.trim() + ".MainPanel");
				if (isInner) {
					JPanel mainPanel = (JPanel) ReflectUtil.newInstance(clazz);
					SwingUtilities.invokeLater(() -> {
						createShowTip(mainPanel);
					});
				} else {
					Method mainMethod = clazz.getDeclaredMethod("main", String[].class);
					mainMethod.invoke(null, (Object) new String[] {});
				}

			} catch (Exception e) {
				String error = ExceptionUtil.getMessage(e);
				error = error.replace("\n", "<br/>");
				JOptionPane.showMessageDialog(null, "<html>" + packageName + " Tip 打开异常:<br>" + error, "Error!",
						JOptionPane.WARNING_MESSAGE);
			}
		});
	}

	private void createShowTip(JPanel mainPanel) {
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
				| UnsupportedLookAndFeelException ex) {
			ex.printStackTrace();
			Toolkit.getDefaultToolkit().beep();
		}
		JFrame frame = new JFrame("@title@");
		frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
		frame.getContentPane().add(mainPanel);
		frame.pack();
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);
		frames.add(frame);
	}
}
